package teemo

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"github.com/bytedance/sonic"
	"github.com/go-resty/resty/v2"
)

// Request 泛型 HTTP 请求结构体，封装了请求参数、头部、调试开关和日志。
// T 为响应体类型，支持泛型解析。
type Request[T interface{}] struct {
	client  *Client
	headers []interface{}
	debug   bool
	logger  Logger
}

// SetHeaders 设置请求头信息，可多次调用叠加不同格式的头部。
// headers: 支持结构体、map等多种格式，自动转换为字符串键值对。
// 返回值: *Request[T] 支持链式调用。
func (c *Request[T]) SetHeaders(headers interface{}) *Request[T] {
	c.headers = append(c.headers, headers)
	return c
}

// SetDebug 开启或关闭调试模式，调试模式下日志级别为 Debug。
// debug: 是否开启调试。
// 返回值: *Request[T] 支持链式调用。
func (c *Request[T]) SetDebug(debug bool) *Request[T] {
	if c.debug == debug {
		return c
	}
	c.logger = c.logger.LogMode(Debug)
	return c
}

// Post 发送 HTTP POST 请求。
// ctx: 上下文对象。
// url: 请求地址。
// data: 请求体数据。
// 返回值: 泛型 T 的响应结果，error 错误信息。
func (c *Request[T]) Post(ctx context.Context, url string, data interface{}) (result T, err error) {
	req := c.client.resty.R().SetContext(ctx).SetBody(data)
	err = setHeaders(c.headers, req)
	if err != nil {
		return
	}

	c.client.logger.Debugf(ctx, printRequest(url, http.MethodPost, c.headers, data))
	defer c.client.logger.Slowf(ctx, time.Now(), url)

	resp, err := req.Post(url)
	if err != nil {
		return
	}

	return handleResp[T](ctx, resp, c.client.logger)
}

// Get 发送 HTTP GET 请求。
// ctx: 上下文对象。
// url: 请求地址。
// data: 查询参数（结构体、map等，自动转换为 query string）。
// 返回值: 泛型 T 的响应结果，error 错误信息。
func (c *Request[T]) Get(ctx context.Context, url string, data interface{}) (result T, err error) {
	params, err := StructJsonTagToStrMap(data)
	if err != nil {
		return
	}

	req := c.client.resty.R().SetContext(ctx).SetQueryParams(params)
	err = setHeaders(c.headers, req)
	if err != nil {
		return
	}

	c.client.logger.Debugf(ctx, printRequest(url, http.MethodGet, c.headers, data))
	defer c.client.logger.Slowf(ctx, time.Now(), url)

	resp, err := req.Get(url)
	if err != nil {
		return
	}

	return handleResp[T](ctx, resp, c.client.logger)
}

// Put 发送 HTTP PUT 请求。
// ctx: 上下文对象。
// url: 请求地址。
// data: 请求体数据。
// 返回值: 泛型 T 的响应结果，error 错误信息。
func (c *Request[T]) Put(ctx context.Context, url string, data interface{}) (result T, err error) {
	req := c.client.resty.R().SetContext(ctx).SetBody(data)
	err = setHeaders(c.headers, req)
	if err != nil {
		return
	}

	c.client.logger.Debugf(ctx, printRequest(url, http.MethodPut, c.headers, data))
	defer c.client.logger.Slowf(ctx, time.Now(), url)

	resp, err := req.Put(url)
	if err != nil {
		return
	}

	return handleResp[T](ctx, resp, c.client.logger)
}

// Delete 发送 HTTP DELETE 请求。
// ctx: 上下文对象。
// url: 请求地址。
// data: 请求体数据。
// 返回值: 泛型 T 的响应结果，error 错误信息。
func (c *Request[T]) Delete(ctx context.Context, url string, data interface{}) (result T, err error) {
	req := c.client.resty.R().SetContext(ctx).SetBody(data)
	err = setHeaders(c.headers, req)
	if err != nil {
		return
	}

	c.client.logger.Debugf(ctx, printRequest(url, http.MethodDelete, c.headers, data))
	defer c.client.logger.Slowf(ctx, time.Now(), url)

	resp, err := req.Delete(url)
	if err != nil {
		return
	}

	return handleResp[T](ctx, resp, c.client.logger)
}

// setHeaders 将多组头部信息设置到 resty.Request 对象中，支持多种格式自动转换。
// headers: 头部信息切片（结构体、map等）。
// req: resty 请求对象。
// 返回值: error，如转换或设置失败则返回错误。
func setHeaders(headers []interface{}, req *resty.Request) error {
	if headers == nil {
		return nil
	}

	for i := range headers {
		strMap, err := StructJsonTagToStrMap(headers[i])
		if err != nil {
			return err
		}
		req.SetHeaders(strMap)
	}

	return nil
}

// handleResp 处理 HTTP 响应，解析响应体并记录日志。
// ctx: 上下文对象。
// resp: resty 响应对象。
// log: 日志记录器。
// 返回值: 泛型 T 的解析结果，error 错误信息。
func handleResp[T interface{}](ctx context.Context, resp *resty.Response, log Logger) (result T, err error) {
	if log == nil {
		log = globalLogger
	}

	// 记录原始响应
	log.Debugf(ctx, `{"url": "%s", "status": %d, "sourceResponse": "%s"}`,
		resp.Request.RawRequest.URL.Path,
		resp.StatusCode(),
		string(resp.Body()),
	)

	// 处理非成功状态码
	if !resp.IsSuccess() {
		err = ErrorMap.Get(resp.StatusCode())
		log.Debugf(ctx, `{"url": "%s", "error": "%v"}`,
			resp.Request.RawRequest.URL.Path,
			err,
		)
		return
	}

	// 解析响应体
	if err = sonic.Unmarshal(resp.Body(), &result); err != nil {
		log.Debugf(ctx, `{"url": "%s", "error": "unmarshal failed: %v"}`,
			resp.Request.RawRequest.URL.Path,
			err,
		)
		return
	}

	// 记录处理后的结果
	body, _ := sonic.Marshal(result)
	log.Debugf(ctx, `{"url": "%s", "result": %s}`,
		resp.Request.RawRequest.URL.Path,
		string(body),
	)

	return result, nil
}

func printRequest(url, method string, headers, body interface{}) string {
	var jsonHeaders, jsonData []byte
	if headers != nil {
		jsonHeaders, _ = sonic.Marshal(headers)
	}
	if body != nil {
		jsonData, _ = sonic.Marshal(body)
	}

	return fmt.Sprintf(`{"method": "%s", "url": "%s", "headers": %s, "body": %s}`,
		method,
		url,
		string(jsonHeaders),
		string(jsonData),
	)
}
